package vg.implementation.ll_services.data_base_service;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import vg.interfaces.ll_services.data_base_service.data.header.*;

import java.util.List;
import java.util.Map;

/**
 * @author Timur Zolotuhin (e-mail: tzolotuhin@gmail.com)
 */
public class SQLiteCacheDataBase {
    // define max for elements, which place in cache
    private static final int MAX_GRAPH_MODEL_CACHE = 100;
    private static final int MAX_GRAPH_CACHE       = 100000;
    private static final int MAX_VERTEX_CACHE      = 100000;
    private static final int MAX_EDGE_CACHE        = 100000;
    private static final int MAX_ATTRIBUTE_CACHE   = 100000;

    // cache for graph elements
    private Map<Integer, GraphModelHeader> cache_graph_models;
    private Map<Integer, GraphHeader> cache_graphs;
    private Map<Integer, VertexHeader> cache_vertices;
    private Map<Integer, EdgeHeader> cache_edges;
    private Map<Integer, AttributeHeader> cache_attributes;

    // counters
    private int graphModelIdCounter;
    private int graphIdCounter;
    private int vertexIdCounter;
    private int edgeIdCounter;
    private int attributeIdCounter;

    // database data
    private SQLiteDataBase_1_0 dataBase;

    public SQLiteCacheDataBase() {
        dataBase = new SQLiteDataBase_1_0();

        // cache
        cache_graph_models = Maps.newHashMap();
        cache_graphs = Maps.newHashMap();
        cache_vertices = Maps.newHashMap();
        cache_edges = Maps.newHashMap();
        cache_attributes = Maps.newHashMap();

        // counters
        graphModelIdCounter = 0;
        graphIdCounter = 0;
        vertexIdCounter = 0;
        edgeIdCounter = 0;
        attributeIdCounter = 0;
    }

    public int createGraphModelHeader(String name)  {
        GraphModelHeader gh = new GraphModelHeader(graphModelIdCounter++);
        gh.setName(name);
        cache_graph_models.put(gh.getId(), gh);
        checkAndCommit();

        return gh.getId();
    }

    public int createGraphHeader(int graphModelId, boolean directed, boolean root) {
        GraphHeader graphHeader = new GraphHeader(graphIdCounter++, graphModelId, directed, root);
        cache_graphs.put(graphHeader.getId(), graphHeader);
        checkAndCommit();

        return graphHeader.getId();
    }

    public int createVertexHeader(int subgraph_id, int innerGraphId) {
        VertexHeader vh = new VertexHeader(vertexIdCounter++, subgraph_id);
        vh.setLink2InnerGraph(innerGraphId);
        cache_vertices.put(vh.getId(), vh);
        checkAndCommit();

        return vh.getId();
    }

    public int createEdgeHeader(int graphId, int sourceVertexId, int targetVertexId) {
        EdgeHeader eh = new EdgeHeader(edgeIdCounter++, graphId, sourceVertexId, targetVertexId);
        cache_edges.put(eh.getId(), eh);
        checkAndCommit();

        return eh.getId();
    }

    public int createEdgeAttributeHeader(int edgeId, String name, String strValue, int type) {
        AttributeHeader attributeHeader;
        attributeHeader = AttributeHeader.createEdgeAttributeHeader(attributeIdCounter++, edgeId);
        attributeHeader.setName(name);
        attributeHeader.setValue(strValue, type);
        cache_attributes.put(attributeHeader.getId(), attributeHeader);
        checkAndCommit();
        return attributeHeader.getId();
    }

    public int createVertexAttributeHeader(int vertexId, String name, String strValue, int type) {
        AttributeHeader attributeHeader = AttributeHeader.createVertexAttributeHeader(attributeIdCounter++, vertexId);
        attributeHeader.setName(name);
        attributeHeader.setValue(strValue, type);
        cache_attributes.put(attributeHeader.getId(), attributeHeader);
        checkAndCommit();
        return attributeHeader.getId();
    }

    public int createGraphAttributeHeader(int graphId, String name, String strValue, int type) {
        AttributeHeader attributeHeader;
        attributeHeader = AttributeHeader.createGraphAttributeHeader(attributeIdCounter++, graphId);
        attributeHeader.setName(name);
        attributeHeader.setValue(strValue, type);
        cache_attributes.put(attributeHeader.getId(), attributeHeader);
        checkAndCommit();
        return attributeHeader.getId();
    }

    public VertexHeader getVertexHeader(int vertexId) {
        VertexHeader vertexHeader = cache_vertices.get(vertexId);
        if(vertexHeader != null) {
            return vertexHeader.clone();
        } else {
            return dataBase.getVertexHeader(vertexId);
        }
    }

    public VertexHeader getVertexHeaderByInnerGraphId(int graphId) {
        for (VertexHeader vertexHeader : cache_vertices.values()) {
            if (vertexHeader.getLink2InnerGraph() == graphId)
                return vertexHeader.clone();
        }
        return dataBase.getVertexHeaderByGraphId(graphId);
    }

    public List<VertexHeader> getVertexHeadersByGraphId(int graphId) {
        List<VertexHeader> result = Lists.newArrayList();

        for (VertexHeader vertexHeader : cache_vertices.values()) {
            if (vertexHeader.getGraphId() == graphId)
                result.add(vertexHeader.clone());
        }
        //result.addAll(dataBase.getVertexHeadersByGraphId(graphId));
        return result;
    }

    public EdgeHeader getEdgeHeader(int edgeId) {
        EdgeHeader eh = cache_edges.get(edgeId);
        if(eh != null) {
            return eh.clone();
        } else {
            return dataBase.getEdgeHeader(edgeId);
        }
    }

    public List<EdgeHeader> getEdgeHeadersByGraphId(int graphId) {
        List<EdgeHeader> result = Lists.newArrayList();

        for (EdgeHeader edgeHeader : cache_edges.values()) {
            if (edgeHeader.getGraphId() == graphId)
                result.add(edgeHeader.clone());
        }
        //result.addAll(dataBase.getVertexHeadersByGraphId(graphId));
        return result;
    }

    public AttributeHeader getAttributeHeader(int attributeId) {
        AttributeHeader attributeHeader = cache_attributes.get(attributeId);
        if(attributeHeader != null) {
            return attributeHeader.clone();
        } else {
            return dataBase.getAttributeHeader(attributeId);
        }
    }

    public List<AttributeHeader> getAttributeHeadersByOwner(int ownerId, int ownerType) {
        List<AttributeHeader> result = Lists.newArrayList();

        for (AttributeHeader header : cache_attributes.values()) {
            if (header.getOwnerId() == ownerId && header.getOwnerType() == ownerType)
                result.add(header);
        }
        result.addAll(dataBase.getAttributeHeadersByOwner(ownerId, ownerType));
        return result;
    }

    public GraphModelHeader getGraphModelHeader(int graphModelId) {
        GraphModelHeader gh = cache_graph_models.get(graphModelId);
        if(gh != null) {
            return gh.clone();
        } else {
            return dataBase.getGraphModelHeader(graphModelId);
        }
    }

    public GraphHeader getGraphHeader(int graphId) {
        GraphHeader gh = cache_graphs.get(graphId);
        if(gh != null) {
            return gh.clone();
        } else {
            return dataBase.getGraphHeader(graphId);
        }
    }

    public List<GraphHeader> getRootGraphHeadersByGraphModelId(int graphModelId) {
        List<GraphHeader> result = Lists.newArrayList();

        for (GraphHeader graphHeader : cache_graphs.values()) {
            if (graphHeader.getGraphModelId() == graphModelId && graphHeader.isRoot())
                result.add(graphHeader.clone());
        }

        return result;
    }

    public List<GraphModelHeader> getGraphModelHeaders(int count) {
        List<GraphModelHeader> result = Lists.newArrayList();

        for (GraphModelHeader header : cache_graph_models.values()) {
            result.add(header);
        }

        result.addAll(dataBase.getGraphModelHeaders(count));

        return result;
    }

    public void close() {
        dataBase.close();
    }

//=============================================================================
//--------------Modifying methods----------------------------------------------
    public boolean modifyGraphModelHeader(GraphModelHeader newGraphModelHeader) {
        GraphModelHeader old_graph_header = cache_graph_models.get(newGraphModelHeader.getId());

        if(old_graph_header != null) {
            cache_graph_models.put(newGraphModelHeader.getId(), newGraphModelHeader);
            return true;
        } else {
            return dataBase.modifyGraphModelHeader(newGraphModelHeader);
        }
    }

    public boolean modifyVertexHeader(VertexHeader newVertexHeader) {
        VertexHeader old_vertex_header = cache_vertices.get(newVertexHeader.getId());

        if(old_vertex_header != null) {
            cache_vertices.put(newVertexHeader.getId(), newVertexHeader);
            return true;
        } else {
            return dataBase.modifyVertexHeader(newVertexHeader);
        }
    }

    public boolean modifyAttributeHeader(AttributeHeader newAttributeHeader) {
        AttributeHeader old_attr_header = cache_attributes.get(newAttributeHeader.getId());
        if(old_attr_header != null) {
            cache_attributes.put(newAttributeHeader.getId(), newAttributeHeader);
            return true;
        } else {
            return dataBase.modifyAttributeHeader(newAttributeHeader);
        }
    }

//=============================================================================
//--------------PRIVATE METHODS------------------------------------------------
    private void checkAndCommit() {
//        if(cache_graph_models.size() > MAX_GRAPH_MODEL_CACHE) {
//            dataBase.addGraphModelHeaders(cache_graph_models.values());
//            cache_graph_models.clear();
//        }
//
//        if(cache_graphs.size() > MAX_GRAPH_CACHE) {
//            dataBase.addGraphHeaders(cache_graphs.values());
//            cache_graphs.clear();
//        }
//
//        if(cache_vertices.size() > MAX_VERTEX_CACHE) {
//            dataBase.addVertexHeaders(cache_vertices.values());
//            cache_vertices.clear();
//        }
//
//        if(cache_edges.size() > MAX_EDGE_CACHE) {
//            dataBase.addEdgeHeaders(cache_edges.values());
//            cache_edges.clear();
//        }
//
//        if(cache_attributes.size() > MAX_ATTRIBUTE_CACHE) {
//            dataBase.addAttributeHeaders(cache_attributes.values());
//            cache_attributes.clear();
//        }
    }

    private void commitAll() {
//        dataBase.addGraphModelHeaders(cache_graph_models.values());
//        cache_graph_models.clear();
//
//        dataBase.addGraphHeaders(cache_graphs.values());
//        cache_graphs.clear();
//
//        dataBase.addVertexHeaders(cache_vertices.values());
//        cache_vertices.clear();
//
//        dataBase.addEdgeHeaders(cache_edges.values());
//        cache_edges.clear();
//
//        dataBase.addAttributeHeaders(cache_attributes.values());
//        cache_attributes.clear();
    }
}
